package com.nts.bupna;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;

import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.Tree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.nts.bupna.errors.ConsoleReporter;
import com.nts.bupna.errors.Reporter;
import com.nts.bupna.model.Process;
import com.nts.bupna.util.AstPrinter;

public class Bupna {
    static Logger log = LoggerFactory.getLogger(Bupna.class);

    private final Reporter reporter;

    public Bupna() {
        this(new ConsoleReporter());
    }

    public Bupna(final Reporter reporter) {
        this.reporter = reporter;
    }

    public Process parse(final String contents) {
        return parse(new ANTLRStringStream(contents));
    }

    public Process parse(final String file, final String encoding) throws IOException {
        return parse(new ANTLRFileStream(file, encoding));
    }

    public Process parse(final InputStream input, final String encoding) throws IOException {
        return parse(new ANTLRInputStream(input, encoding));
    }

    public Process parse(final Reader reader) throws IOException {
        return parse(new ANTLRReaderStream(reader));
    }

    Process parse(final CharStream charStream) {
        final BLexer blexer = new BLexer();
        blexer.setReporter(reporter);
        blexer.setCharStream(charStream);
        final CommonTokenStream tokens = new CommonTokenStream(blexer);
        final BParser parser = new BParser(tokens);
        parser.setReporter(reporter);
        try {
            final Tree t = (Tree) parser.process().getTree();

            if (log.isDebugEnabled()) {
                final String stringTree = t.toStringTree();
                log.debug(stringTree);
                final StringWriter out = new StringWriter();
                AstPrinter.showAst(t, new PrintWriter(out));
                log.debug("\n" + out.toString());
            }

            if (blexer.getNumberOfSyntaxErrors() != 0) {
                throw new BupnaException("Failed to lex, check log for errors");
            }
            if (parser.getNumberOfSyntaxErrors() != 0) {
                throw new BupnaException("Failed to parse, check log for errors");
            }

            final BTree walker = new BTree(new CommonTreeNodeStream(t));
            walker.setReporter(reporter);
            log.debug("AST Walk Start");
            final Process process = walker.proc();
            if (walker.getNumberOfSyntaxErrors() != 0) {
                throw new BupnaException("Failed to walk AST, check log for errors");
            }
            log.debug("\n{}", process);
            return process;
        } catch (final RecognitionException e) {
            throw new BupnaException(e);
        }
    }
}
